﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using Chat = System.Net;
using System.Threading;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using ChatServerApp.Tools;


namespace ChatServerApp.Classes
{
    class CommunicationHandling
    {
        Chat.Sockets.TcpClient client;
        System.IO.StreamReader reader;
        System.IO.StreamWriter writer;
        public delegate void SendMessageEventHandler(object sender, MessageEventArgs mEA);
        public static event SendMessageEventHandler OnSendMessageHandler;
        List<User_Complete> userObject;

        string nickName;
        string pw;
        int serverPort;

        public CommunicationHandling(System.Net.Sockets.TcpClient tcpClient)
        {
            userObject = ActiveComponents.UserList;
            client = tcpClient;
            Thread loginThread = new Thread(new ThreadStart(handleClients));
            loginThread.Start();
        }

        private string GetNextLine()
        {
            return reader.ReadLine();
        }

        private void handleClients()
        {
            reader = new System.IO.StreamReader(client.GetStream());
            writer = new System.IO.StreamWriter(client.GetStream());

            writer.WriteLine("Welcome to HWR-Chat");
            writer.Flush();
            string request = reader.ReadLine();

            raiseEvent("Client Connected. Reason: " + request);


            switch (request)
            {
                case "REGISTER":
                    register();
                    break;
                case "LOGIN":
                    login();
                    break;
                case "LOGOUT":
                    logout();
                    break;
                case "GET IP":
                    getIP();
                    break;
                case "IS ONLINE":
                    onlineStatus();
                    break;
                case "SEARCH USER":
                    returnSearchedUser();
                    break;
                case "ADD CONTACT":
                    addContact();
                    Tools.ActiveComponents.Update();
                    break;
                case "REMOVE CONTACT":
                    removeContact();
                    Tools.ActiveComponents.Update();
                    break;
                case "GET CONTACT COUNT":
                    getContactCount();
                    break;
                case "GET CONTACTS":
                    getContacts();
                    break;
                case "IS FREE NICK":
                    isFreeNick();
                    break;
                default:
                    break;
            }

        }

        void register()
        {
            // sends ACK
            writer.WriteLine("ACK");
            writer.Flush();

            // Reads in User
            string name = reader.ReadLine();
            string vorname = reader.ReadLine();
            string nick = reader.ReadLine();
            string pw = reader.ReadLine();
            string jahrgang = reader.ReadLine();
            string email = reader.ReadLine();

            User_Complete result = getUserComplete(nick);

            if (result == null)
            {
                // ready
                writer.WriteLine("ACK");
                writer.Flush();

                User_Complete user = new User_Complete(name, vorname, email, pw, nick, jahrgang);
                ActiveComponents.UserAdd(user);
            }
            else
            {
                writer.WriteLine("NEG");
                writer.Flush();

                writer.WriteLine("User already exists...");
                writer.Flush();
            }
        }

        private User_Complete getUserComplete(string nickname)
        {
            User_Complete result = userObject.Find(
                delegate(User_Complete user)
                {
                    return user.Nick == nickname;
                }
                );
            return result;
        }

        private User getUser(string nickname)
        {
            User_Complete result = userObject.Find(
                delegate(User_Complete user)
                {
                    return user.Nick == nickname;
                }
                );
            User resu = null;

            if (result != null)
                resu = result.getUser();

            return resu;
        }

        void login()
        {
            writer.WriteLine("ACK");
            writer.Flush();
            nickName = reader.ReadLine();
            pw = reader.ReadLine();
            serverPort = Convert.ToInt32(reader.ReadLine());

            User_Complete user = getUserComplete(nickName);

            if (user != null && user.Pw == pw)
            {
                writer.WriteLine("ACK");
                writer.WriteLine("Login Accepted.");
                writer.Flush();
                raiseEvent("Client connected: " + nickName + ", pw: " + pw + ", ip: " + IPAddress.Parse(((IPEndPoint)client.Client.RemoteEndPoint).Address.ToString()) + ", port: " + serverPort);
                user.Update(IPAddress.Parse(((IPEndPoint)client.Client.RemoteEndPoint).Address.ToString()).ToString(), serverPort.ToString(), true);

                string test = SerializeObjectToXmlString<User_Complete>(user);
                writer.WriteLine(test);
                writer.Flush();
                writer.Close();
                
            }
            else
            {
                writer.WriteLine("NEG");
                writer.WriteLine("Login Rejected.");
                writer.Flush();
                raiseEvent("Client could not get connected: " + nickName + ", pw: " + pw);
            }

        }

        void logout()
        {
            writer.WriteLine("ACK");
            writer.Flush();
            nickName = reader.ReadLine();

            User_Complete user = getUserComplete(nickName);

            if (user != null)
            {
                user.Update(String.Empty, String.Empty, false);

                writer.WriteLine("ACK");
                writer.Flush();
                raiseEvent("Client logged out: " + nickName);
            }
        }

        void getIP()
        {
            writer.WriteLine("ACK");
            writer.Flush();
            nickName = reader.ReadLine();
            User_Complete user = getUserComplete(nickName);

            if (user != null)
            {
                writer.WriteLine(user.Ip.ToString());
                writer.WriteLine(user.Port.ToString());
                writer.WriteLine(user.IsOnline.ToString());
                writer.Flush();
            }
        }

        void onlineStatus()
        {
            writer.WriteLine("ACK");
            writer.Flush();

            string nickNameRequest = reader.ReadLine();

            try
            {
                while (nickNameRequest != "FINISHED")
                {
                    User_Complete user = getUserComplete(nickNameRequest);
                    if (user != null)
                    {
                        writer.WriteLine(user.IsOnline.ToString());
                        writer.Flush();
                    }
                    nickNameRequest = reader.ReadLine();
                }
                writer.WriteLine("ACK");
                writer.Flush();

            }
            catch(Exception e)
            {
                File.WriteAllText("error_serv.log", e.Message);
            }
        }

        void returnSearchedUser()
        {
            writer.WriteLine("ACK");
            writer.Flush();

            string nickNameRequest = reader.ReadLine();

            try
            {
               
                User user = getUser(nickNameRequest);

                if (user != null)
                {
                    writer.WriteLine("1");
                    writer.Flush();
                    string test = SerializeObjectToXmlString<User>(user);
                    writer.WriteLine(test);
                    writer.Flush();
                    writer.Close();
                }
                else
                {
                    writer.WriteLine("0");
                    writer.Flush();
                    writer.WriteLine("NO USER FOUND");
                    writer.Flush();
                    writer.Close();
                }

            }
            catch (Exception e)
            {
                File.WriteAllText("error_serv.log", e.Message);
            }
        }

        void addContact()
        {
            writer.WriteLine("ACK");
            writer.Flush();

            string nickNameRequest = reader.ReadLine();

            try
            {
                while (nickNameRequest != "FINISHED")
                {
                    string nick = nickNameRequest.Substring(0, nickNameRequest.IndexOf("|"));
                    string newContact = nickNameRequest.Substring(nickNameRequest.IndexOf("|") + 1);
                    User_Complete user = getUserComplete(nick);
                    user.Contacts.Add(newContact);
                    user = getUserComplete(newContact);
                    user.Contacts.Add(nick);
                    nickNameRequest = reader.ReadLine();
                }
                writer.WriteLine("ACK");
                writer.Flush();

            }
            catch (Exception e)
            {
                File.WriteAllText("error_serv.log", e.Message);
            }
        }

        void removeContact()
        {
            writer.WriteLine("ACK");
            writer.Flush();

            string nickNameRequest = reader.ReadLine();

            try
            {
                while (nickNameRequest != "FINISHED")
                {
                    string nick = nickNameRequest.Substring(0, nickNameRequest.IndexOf("|"));
                    string deleteContact = nickNameRequest.Substring(nickNameRequest.IndexOf("|") + 1);
                    User_Complete user = getUserComplete(nick);
                    user.Contacts.Remove(deleteContact);
                    user = getUserComplete(deleteContact);
                    user.Contacts.Remove(nick);
                    nickNameRequest = reader.ReadLine();
                }
                writer.WriteLine("ACK");
                writer.Flush();

            }
            catch (Exception e)
            {
                File.WriteAllText("error_serv.log", e.Message);
            }
        }

        void getContactCount()
        {
            writer.WriteLine("ACK");
            writer.Flush();

            try
            {
                string nickNameRequest = reader.ReadLine();
                User_Complete user = getUserComplete(nickNameRequest);
                if (user != null)
                {
                    writer.WriteLine(user.Contacts.Count);
                    writer.Flush();
                }
                else
                {
                    writer.WriteLine("0");
                    writer.Flush();
                }

            }
            catch (Exception e)
            {
                File.WriteAllText("error_serv.log", e.Message);
            }
        }

        void getContacts()
        {
            writer.WriteLine("ACK");
            writer.Flush();

            try
            {
                string nickNameRequest = reader.ReadLine();
                User_Complete user = getUserComplete(nickNameRequest);
                if (user != null)
                {
                    foreach (string element in user.Contacts.ToArray())
                    {
                        writer.WriteLine(element);
                        writer.Flush();
                    }
                    writer.WriteLine("FINISHED");
                    writer.Flush();
                }

            }
            catch (Exception e)
            {
                File.WriteAllText("error_serv.log", e.Message);
            }
        }

        void isFreeNick()
        {
            writer.WriteLine("ACK");
            writer.Flush();

            try
            {
                string nickNameRequest = reader.ReadLine();
                User_Complete user = getUserComplete(nickNameRequest);
                if (user != null)
                {
                    writer.WriteLine("1");
                    writer.Flush();
                }
                else
                {
                    writer.WriteLine("0");
                    writer.Flush();
                }

            }
            catch (Exception e)
            {
                File.WriteAllText("error_serv.log", e.Message);
            }
        }

        #region Serialization
        private string SerializeObjectToXmlString<T>(T objectToSerialize)
        {
            XmlSerializer ser = new XmlSerializer(typeof(T));
            StringBuilder sb = new StringBuilder();
            StringWriter sb_wr = new StringWriter(sb);
            ser.Serialize(sb_wr, objectToSerialize);
            return sb.ToString();

        }

        private T DeserializeXmlString<T>(string xmlStringObject)
        {
            XmlSerializer ser = new XmlSerializer(typeof(T));
            StringReader re = new StringReader(xmlStringObject);
            XmlTextReader rea = new XmlTextReader(re);
            // StreamReader sr = new StreamReader(rea);
            return (T)ser.Deserialize(rea);
        }
        #endregion

        #region Messaging

        void raiseEvent(String mess)
        {
            this.OnSendMessage(new MessageEventArgs(mess));
        }

        protected void OnSendMessage(MessageEventArgs e)
        {
            if (OnSendMessageHandler != null)
                OnSendMessageHandler(this, e);
        }

        #endregion

    }
}
